package com.study.chunguard.common.service.impl;

import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.study.chunguard.common.exception.IdempotencyException;
import com.study.chunguard.common.exception.LockException;
import com.study.chunguard.common.lock.DistributedLock;
import com.study.chunguard.common.lock.LockCondition;
import com.study.chunguard.common.lock.ZLock;
import com.study.chunguard.common.service.ISuperService;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * service实现父类
 *
 * @author zhaowenhao
 * @since 2022-12-10
 * <p>
 */
public abstract class SuperServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements ISuperService<T> {

    @SafeVarargs
    @Override
    public final boolean saveIdempotency(T entity, DistributedLock locker, LockCondition<T>... lockConditions) {
        if (locker == null) {
            throw new LockException("DistributedLock is null");
        }

        List<ZLock> locks = new ArrayList<>();
        try {
            checkRepeat(locker, locks, lockConditions);
            return super.save(entity);
        } catch (Exception e) {
            throw new IdempotencyException(e.getMessage());
        } finally {
            for (ZLock lock : locks) {
                try {
                    lock.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    @SafeVarargs
    @Override
    public final boolean updateByIdIdempotency(T entity, DistributedLock locker, LockCondition<T>... lockConditions) {
        if (locker == null) {
            throw new LockException("DistributedLock is null");
        }

        List<ZLock> locks = new ArrayList<>();
        try {
            checkRepeat(locker, locks, lockConditions);
            return super.updateById(entity);
        } catch (Exception e) {
            throw new IdempotencyException(e.getMessage());
        } finally {
            for (ZLock lock : locks) {
                try {
                    lock.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    private void checkRepeat(DistributedLock locker, List<ZLock> locks, LockCondition<T>[] lockConditions) throws Exception {
        for (LockCondition<T> condition : lockConditions) {
            if (condition.isEnabled()) {
                ZLock lock = locker.tryLock(condition.getLockKey(), 10, 60, TimeUnit.SECONDS);
                if (lock == null) {
                    throw new LockException("锁等待超时");
                }
                locks.add(lock);
                long count = super.count(condition.getQueryWrapper());
                if (count > 0) {
                    throw new IdempotencyException(condition.getMsg());
                }
            }
        }
    }

    @SafeVarargs
    @Override
    public final boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, LockCondition<T>... lockConditions) {
        if (null != entity) {
            Class<?> cls = entity.getClass();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
            if (null != tableInfo && CharSequenceUtil.isNotEmpty(tableInfo.getKeyProperty())) {
                Object idVal = ReflectionKit.getFieldValue(entity, tableInfo.getKeyProperty());
                if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {
                    return this.saveIdempotency(entity, lock, lockConditions);
                } else {
                    return updateByIdIdempotency(entity, lock, lockConditions);
                }
            } else {
                throw new IdempotencyException("找不到有效的@TableId字段.");
            }
        }
        return false;
    }
}
